package autosession

import (
	"context"
	"errors"
	"time"

	"github.com/redis/go-redis/v9"
)

// RedisStore stores sessions in a redis backend.
type RedisStore struct {
	rdCmd         redis.Cmdable
	DefaultMaxAge int // default Redis TTL for a MaxAge == 0 session
	maxLength     int
	keyPrefix     string
	serializer    SessionSerializer
}

func (s *RedisStore) SetMaxLength(l int) {
	if l >= 0 {
		s.maxLength = l
	}
}

// SetKeyPrefix set the prefix
func (s *RedisStore) SetKeyPrefix(p string) {
	s.keyPrefix = p
}

func (s *RedisStore) Get(ctx context.Context, sessionId string) (map[string]string, error) {
	return s.load(ctx, sessionId)
}

func (s *RedisStore) Save(ctx context.Context, sessionId string, values map[string]string, opts *Options) error {
	if opts.MaxAge <= 0 {
		if err := s.delete(ctx, sessionId); err != nil {
			return err
		}
	} else {
		if err := s.save(ctx, sessionId, values, opts); err != nil {
			return err
		}
	}
	return nil
}

func (s *RedisStore) save(ctx context.Context, sessionId string, values map[string]string, opts *Options) error {
	b, err := s.serializer.Serialize(values)
	if err != nil {
		return err
	}
	if s.maxLength != 0 && len(b) > s.maxLength {
		return errors.New("SessionStore: the value to store is too big")
	}
	age := opts.MaxAge
	if age == 0 {
		age = s.DefaultMaxAge
	}
	err = s.rdCmd.SetEx(ctx, s.keyPrefix+sessionId, b, time.Duration(age)*time.Second).Err()
	return err
}

func (s *RedisStore) load(ctx context.Context, sessionId string) (map[string]string, error) {
	data, err := s.rdCmd.Get(ctx, s.keyPrefix+sessionId).Bytes()
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, errors.New("not found")
	}
	return s.serializer.Deserialize(data)
}

func (s *RedisStore) delete(ctx context.Context, sessionId string) error {
	if err := s.rdCmd.Del(ctx, s.keyPrefix+sessionId).Err(); err != nil {
		return err
	}
	return nil
}

func NewRedisStore(rdCmd redis.Cmdable, keyPrefix string) (*RedisStore, error) {
	rs := &RedisStore{
		rdCmd:      rdCmd,
		maxLength:  4096,
		keyPrefix:  keyPrefix,
		serializer: JSONSerializer{},
	}
	return rs, nil
}
